package com.sunchis.demo.util.server;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class AgentDiscover {
	/**
	 * 连接的缓冲区，如果设置太小且发送方的数据太大，那么数据的接受可能就不是特别完整了！<br>
	 * 从UDP的封装格式来看。其中源端口和目的端口各占16位。数据报长度位占16位。  <br>
	 * 此数据报长度表示的为数据报长度，包含首部和数据区，以字节为单位，最小值为8，即没有数据时的长度。 
	 * 2^16=65536.所以，UDP最大的数据报长度为65536字节。 	*/
	private byte[] buffer = new byte[4096];
	private static MulticastSocket multicastSocket = null;
	private static DatagramSocket datagramSocket = null;
	private DatagramPacket packet = null;
	
	private InetAddress multicastGroup;
	private String defaultMulticastIp = "224.0.0.4";
	private int defaultMulticastPort = 6006;
	
	/**
	 * 组播地址	*/
	private String multicastIp;
	/**
	 * 组播地址端口	*/
	private int multicastPort;
	
	public AgentDiscover(String multicastIp) {
		new AgentDiscover(multicastIp, defaultMulticastPort);
	}
	
	/**
	 * 创建一个用于发送和接收的MulticastSocket组播套接字对象
	 * @param multicastIp
	 * @param multicastPort
	 */
	public AgentDiscover(String multicastIp, int multicastPort) {
		setMulticastIp(multicastIp);
		setMulticastPort(multicastPort);
		
		try {
			multicastGroup = InetAddress.getByName(getMulticastIp()); 		// 组播地址
			multicastSocket = new MulticastSocket(getMulticastPort());		// 创建一个用于发送和接收的MulticastSocket组播套接字对象
																			// 并绑定到特定端口，如6006
			// multicastSocket.setTimeToLive(64);
			multicastSocket.joinGroup(multicastGroup);						// 加入组播组		
			System.out.println("AgentDiscover引擎启动成功，侦听端口: " + getMulticastPort() + " " + new java.util.Date());
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("AgentDiscover引擎启动失败");
		}
	}
	
	/**
	 * 接收数据包，该方法会造成线程阻塞 
	 * @return
	 * @throws IOException
	 */
	public final String receive() throws IOException {  
        packet = new DatagramPacket(buffer, buffer.length);  	// 创建一个指定缓冲区大小及组播地址和端口的DatagramPacket组播数据包对象
        multicastSocket.receive(packet); 						// 接收组播数据包: 使用组播套接字的receive()方法，将组播数据包对象放入其中
        
        // 解码组播数据包提取信息，并依据得到的信息作出响应 
        String data = new String(packet.getData(), 0, packet.getLength(), "utf-8");
        return data;
    }
	
	/**
	 * 将响应包发送给请求端
	 * @param data
	 * @throws IOException
	 */
//	public final void response(String data) throws IOException {  
//        System.out.println("客户端地址 : " + packet.getAddress().getHostAddress() + ", 端口：" + packet.getPort());
//        datagramSocket = new DatagramSocket();
//        DatagramPacket outPacket = new DatagramPacket(buffer, buffer.length, packet.getAddress(), packet.getPort());  
//        outPacket.setData(data.getBytes());  
//        datagramSocket.send(outPacket);  
//    }
	
	public void close() {
		if (multicastSocket != null) {
			try {
				multicastSocket.leaveGroup(multicastGroup);			// 使用组播套接字的leaveGroup()方法，离开组播组
				multicastSocket.close(); 							// 关闭组播套接字
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("socket closed successfully.");
	}

	/**
	 * 组播地址<br>
	 * 若组播地址为null或未空时，则使用默认给定的组播地址<br>
	 * 默认组播地址为：224.0.0.4
	 * @return 广播地址	*/
	public String getMulticastIp() {
		if (multicastIp == null) {
			multicastIp = defaultMulticastIp;
		} else {
			if (multicastIp.equals("")) {
				multicastIp = defaultMulticastIp;
			}
		}
		return multicastIp;
	}

	/**
	 * 组播地址端口<br>
	 * 若组播地址端口为零（即为指定端口），则使用默认给定的组播端口<br>
	 * 默认组播端口为：6006
	 * @return 广播地址	*/
	public int getMulticastPort() {
		if (multicastPort == 0) {
			multicastPort = defaultMulticastPort;
		}
		return multicastPort;
	}
	
	/**
	 * 设定组播地址
	 * @param multicastIp	*/
	private void setMulticastIp(String multicastIp) {
		this.multicastIp = multicastIp;
	}

	/**
	 * 设定组播地址端口
	 * @param multicastPort
	 */
	private void setMulticastPort(int multicastPort) {
		this.multicastPort = multicastPort;
	}

	public InetAddress getMulticastGroup() {
		return multicastGroup;
	}
}
